<?php

/**
 * System class.
 *
 * @version  $Id: system.php 2 2009-10-02 23:06:43Z perfilev $
 * @package  System
 */

class NotFoundException extends Exception {};
class ForbiddenException extends Exception {};

final class System
{
	/**
	 * @var boolean  Is in CLI?
	 */
	public static $inCli = false;

	/**
	 * @var  array  Paths.
	 */
	public static $paths   = array(APPLICATION_PATH, SYSTEM_PATH);

	/**
	 * @var  array  Modules.
	 */
	public static $modules = array();

	/**
	 * @var  array  Configuration.
	 */
	private static $config = array();

	/**
	 * System initialization.
	 *
	 * Supported options:
	 *   modules - List of modules (associative array)
	 *
	 * @param  array  $config  System's options.
	 * @return void
	 */
	public static function init(array $options = array())
	{
		ob_start();
		self::$inCli = (php_sapi_name() == 'cli');

		// Option 'modules'
		if (array_key_exists('modules', $options)) {

			$paths   = array(APPLICATION_PATH);
			$modules = $options['modules'];
			foreach ($modules as $name => $path) {
				if (is_dir($path)) {
					$paths[] = $path . '/';
				} else {
					unset($modules[$name]);
				}
			}
			$paths[] = SYSTEM_PATH;
			self::$paths   = $paths;
			self::$modules = $modules;

			foreach ($modules as $name => $path) {
				if (is_file($path . '/init.php')) {
					require_once $path . '/init.php';
				}
			}

		}
	}

	/**
	 * Locates the full path of a file by directory and filename.
	 *
	 * When searching the 'config', an array of files will be returned.
	 *
	 * @param  string  $dir   Directory name
	 * @param  string  $file  Filename with subdirectories
	 * @return string|array
	 */
	public static function locate($directory, $filename)
	{
		$filename = $directory . '/' . $filename;
		if ($directory == 'config') {
			$found = array();
			foreach (array_reverse(self::$paths) as $path) {
				if (is_file($path . $filename)) {
					$found[] = $path . $filename;
				}
			}
			return $found;
		} else {
			foreach (self::$paths as $path) {
				if (is_file($path . $filename)) {
					return $path . $filename;
				}
			}
		}
		return false;
	}

	/**
	 * Returns a configure item or group.
	 *
	 * @param  string   $key      Item name.
	 * @param  boolean  $reqyured Whether item is required?
	 * @param  mixed    $default  Default value.
	 * @return mixed
	 * @throws Exception
	 */
	public static function config($key, $required = true, $default = null)
	{
		list($group, $key) = explode('.', $key, 2);
		if (array_key_exists($group, self::$config) == false) {
			self::$config[$group] = self::configLoad($group);
		}
		if (array_key_exists($key, self::$config[$group])) {
			return self::$config[$group][$key];
		}
		if ($required) {
			throw new Exception('Config key "' . $key . '" does not exist in group "' . $group . '".');
		}
		return $default;
	}

	/**
	 * Loads a config file.
	 *
	 * @param  string  $name  Config filename.
	 * @return array
	 * @throws Exception
	 */
	private static function configLoad($name)
	{
		if ($name === 'core') {
			return require APPLICATION_PATH . 'config/config.php';
		}
		$config = array();
		if ($files = self::locate('config', $name . '.php')) {
			foreach ($files as $file) {
				$config = array_merge($config, require $file);
			}
		}
		return $config;
	}

	/**
	 * Generates a random string of a given type and length.
	 *
	 * Supported types:
	 *   alphanum  Alpha-numeric characters;
	 *   alpha     Alphabetical characters;
	 *   hexdec    Hexadecimal characters, 0-9 plus a-f;
	 *   numeric   Digit characters, 0-9;
	 *   distinct  Clearly distinct alpha-numeric characters.
	 *
	 * @param  string   $type    A type of pool, or a string of characters to use as the pool.
	 * @param  integer  $length  Length of string to return.
	 * @return string
	 */
	public static function random($type = 'alphanum', $length = 8)
	{
		switch ($type) {
			case 'alphanum':
				$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
				break;
			case 'alphanuml':
				$pool = '0123456789abcdefghijklmnopqrstuvwxyz';
				break;
			case 'alpha':
				$pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
				break;
			case 'alphal':
				$pool = 'abcdefghijklmnopqrstuvwxyz';
				break;
			case 'hexdec':
				$pool = '0123456789abcdef';
				break;
			case 'numeric':
				$pool = '0123456789';
				break;
			case 'distinct':
				$pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
				break;
			default:
				$pool = (string) $type;
		}
		$pool = str_split($pool, 1);
		$max  = count($pool) - 1;
		$result = '';
		for ($i = 0; $i < $length; $i++) {
			$result .= $pool[mt_rand(0, $max)];
		}
		return $result;
	}

	/**
	 * Prints variable dump.
	 *
	 * @param  string  $var  Variable to dump.
	 * @return void
	 */
	public static function dump($var)
	{
		echo self::$inCli ? "\n@~~\n" : '<div style="display: block; font-family: Courier New; font-size: .8em; line-height: 1em; '
		   . 'white-space: pre; background-color: #eee; margin: .5em 0; padding: .5em;">';
		if (is_array($var)) {
			print_r($var);
		} else {
			var_dump($var);
		}
		echo self::$inCli ? "\n~~.\n" : '</div>';
	}

	/**
	 * Returns website settings
	 *
	 * @return array
	 */
	public static function settings()
	{
		$settings = Database::instance()->query('SELECT * FROM `settings`')->fetchAll();
		$values = array();
		foreach ($settings as $setting) {
			$values[$setting['key']] = $setting['value'];
		}
		unset($setting);
		unset($settings);
		return $values;
	}

}
